package com.zjcloud.framework.config;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;

import org.apache.ibatis.io.VFS;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.type.Alias;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.ClassUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;

import com.zjcloud.common.config.RuoYiConfig;
import com.zjcloud.common.constant.Constants;
import com.zjcloud.common.core.domain.model.LoginUser;
import com.zjcloud.framework.interceptor.SqlStatementInterceptor;
import com.zjcloud.common.utils.StringUtils;
import com.zjcloud.common.utils.spring.SpringUtils;


/**
 * Mybatis支持*匹配扫描包
 *
 * @author ruoyi
 */
@Configuration
public class MyBatisConfig
{
    private static final Logger logger = LoggerFactory.getLogger(MyBatisConfig.class);
    
    @Autowired
    private Environment env;

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";



    public Resource[] resolveMapperLocations(String[] mapperLocations)
    {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<Resource> resources = new ArrayList<Resource>();
        if (mapperLocations != null)
        {
            for (String mapperLocation : mapperLocations)
            {
                try
                {
                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
                    resources.addAll(Arrays.asList(mappers));
                }
                catch (IOException e)
                {
                    // ignore
                }
            }
        }
        return resources.toArray(new Resource[resources.size()]);
    }
    
    /**
     * 设置类型别名包，确保每个模块的包名都是唯一的
     * 处理typeAliasesPackage配置，避免别名冲突
     */
    public static String setTypeAliasesPackage(String typeAliasesPackage)
    {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
        
        // 分割多个包路径
        String[] typeAliasesPackages = org.springframework.util.StringUtils.split(typeAliasesPackage, ",");
        Set<String> resultPackages = new HashSet<>();
        
        try
        {
            for (String pkg : typeAliasesPackages)
            {
                // 标准化包路径
                String packagePath = ClassUtils.convertClassNameToResourcePath(pkg);
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + packagePath + "/**/*.class";
                
                // 查找包下的所有类
                Resource[] resources = resolver.getResources(packageSearchPath);
                for (Resource resource : resources)
                {
                    if (resource.isReadable())
                    {
                        MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                        String className = metadataReader.getClassMetadata().getClassName();
                        
                        // 获取类所在的包
                        int lastDotIndex = className.lastIndexOf('.');
                        if (lastDotIndex > 0)
                        {
                            String classPackage = className.substring(0, lastDotIndex);
                            resultPackages.add(classPackage);
                        }
                    }
                }
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return typeAliasesPackage;
        }
        
        // 将处理后的包路径转换为逗号分隔的字符串
        return org.springframework.util.StringUtils.collectionToCommaDelimitedString(resultPackages);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
    {
        String mapperLocations = env.getProperty("mybatis.mapperLocations");
        String configLocation = env.getProperty("mybatis.configLocation");
        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
        VFS.addImplClass(SpringBootVFS.class);

        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(resolveMapperLocations(org.springframework.util.StringUtils.split(mapperLocations, ",")));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
        
        // 配置驼峰命名转换
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        sessionFactory.setConfiguration(configuration);
        
        // 添加SQL执行拦截器
        SqlStatementInterceptor sqlInterceptor = new SqlStatementInterceptor();
        sessionFactory.setPlugins(new Interceptor[]{ sqlInterceptor });
        
        // 自定义类型别名处理
        if (com.zjcloud.common.utils.StringUtils.isNotEmpty(typeAliasesPackage)) {
            // 使用自定义的TypeAliasRegistry来处理别名冲突
            sessionFactory.setSqlSessionFactoryBuilder(new SqlSessionFactoryBuilder() {
                @Override
                public SqlSessionFactory build(org.apache.ibatis.session.Configuration configuration) {
                    // 在构建SqlSessionFactory之前，处理类型别名
                    processTypeAliases(configuration, typeAliasesPackage);
                    // 专门注册AcSemester别名
                    registerAcSemesterAlias(configuration);
                    return super.build(configuration);
                }
            });
        }
        
        return sessionFactory.getObject();
    }
    
    /**
     * 创建BeanPostProcessor，用于在SqlSessionFactory创建后注册教务管理模块的实体类别名
     */
    @Bean
    public BeanPostProcessor sqlSessionFactoryBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) {
                if (bean instanceof SqlSessionFactory) {
                    SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) bean;
                    org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
                    TypeAliasRegistry typeAliasRegistry = configuration.getTypeAliasRegistry();
                    
                    // 安全地注册别名，只在类存在时才注册
                    safelyRegisterAlias(typeAliasRegistry, "com.zjcloud.academic.domain.AcSemester", "AcSemester");
                    safelyRegisterAlias(typeAliasRegistry, "com.zjcloud.jwgl.domain.AcScheduleStatistics", "AcScheduleStatistics");
                    safelyRegisterAlias(typeAliasRegistry, "com.zjcloud.jwgl.domain.AcConstraintType", "AcConstraintType");
                    safelyRegisterAlias(typeAliasRegistry, "com.zjcloud.jwgl.domain.AcSchoolInfo", "AcSchoolInfo");
                    safelyRegisterAlias(typeAliasRegistry, "com.zjcloud.academic.domain.GenTableColumn", "GenTableColumn");
                }
                return bean;
            }
            
            /**
             * 安全地注册类型别名，只在类存在时才注册
             */
            private void safelyRegisterAlias(TypeAliasRegistry registry, String className, String alias) {
                try {
                    Class<?> clazz = Class.forName(className);
                    registry.registerAlias(alias, clazz);
                    logger.info("成功注册类型别名: {} -> {}", alias, className);
                } catch (ClassNotFoundException e) {
                    logger.debug("类 {} 不存在，跳过别名注册: {}", className, e.getMessage());
                } catch (Exception e) {
                    logger.warn("注册类型别名 {} -> {} 失败: {}", alias, className, e.getMessage());
                }
            }
        };
    }
    
    /**
     * 处理类型别名，解决别名冲突问题
     */
    private void processTypeAliases(org.apache.ibatis.session.Configuration configuration, String typeAliasesPackage) {
        TypeAliasRegistry typeAliasRegistry = configuration.getTypeAliasRegistry();
        
        // 清空默认注册的别名
        try {
            java.lang.reflect.Field aliasesField = TypeAliasRegistry.class.getDeclaredField("TYPE_ALIASES");
            aliasesField.setAccessible(true);
            java.util.Map<String, Class<?>> aliases = (java.util.Map<String, Class<?>>) aliasesField.get(typeAliasRegistry);
            aliases.clear();
        } catch (Exception e) {
            logger.error("清空类型别名失败", e);
        }
        
        // 扫描指定包下的所有类
        String[] typeAliasPackageArray = org.springframework.util.StringUtils.tokenizeToStringArray(typeAliasesPackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        
        for (String packageToScan : typeAliasPackageArray) {
            try {
                ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
                scanner.addIncludeFilter(new AssignableTypeFilter(Object.class));
                
                Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(packageToScan);
                for (BeanDefinition beanDefinition : candidateComponents) {
                    String beanClassName = beanDefinition.getBeanClassName();
                    Class<?> clazz = Class.forName(beanClassName);
                    
                    // 获取包名
                    String packageName = clazz.getPackage().getName();
                    
                    // 只使用"包名.类名"作为别名，确保唯一性
                    String alias = packageName + "." + clazz.getSimpleName();
                    
                    // 为AcSemester类设置特殊别名
                    if ("com.zjcloud.academic.domain.AcSemester".equals(beanClassName)) {
                        typeAliasRegistry.registerAlias("AcSemester", clazz);
                    }
                    
                    // 为AcScheduleTask类设置特殊别名
                    if ("com.zjcloud.academic.domain.AcScheduleTask".equals(beanClassName)) {
                        typeAliasRegistry.registerAlias("AcademicScheduleTask", clazz);
                        typeAliasRegistry.registerAlias("AcScheduleTask", clazz);
                    }
                    
                    // 特殊处理JwglScheduleTask类，确保其别名不会与AcScheduleTask冲突
                    if ("com.zjcloud.jwgl.domain.JwglScheduleTask".equals(beanClassName)) {
                        alias = "com.zjcloud.jwgl.domain.JwglScheduleTask";
                    }
                    
                    // 为AcSchoolInfo类设置特殊别名
                    if ("com.zjcloud.jwgl.domain.AcSchoolInfo".equals(beanClassName)) {
                        typeAliasRegistry.registerAlias("AcSchoolInfo", clazz);
                    }
                    
                    // 为AcScheduleStatistics类设置特殊别名
                    if ("com.zjcloud.jwgl.domain.AcScheduleStatistics".equals(beanClassName)) {
                        typeAliasRegistry.registerAlias("AcScheduleStatistics", clazz);
                    }
                    
                    // 为AcConstraintType类设置特殊别名
                    if ("com.zjcloud.jwgl.domain.AcConstraintType".equals(beanClassName)) {
                        typeAliasRegistry.registerAlias("AcConstraintType", clazz);
                    }
                    
                    try {
                        typeAliasRegistry.registerAlias(alias, clazz);
                        logger.debug("注册类型别名: {} -> {}", alias, clazz.getName());
                    } catch (Exception e) {
                        logger.warn("注册类型别名失败: {} -> {}, 错误: {}", alias, clazz.getName(), e.getMessage());
                    }
                }
            } catch (Exception e) {
                logger.error("扫描类型别名包失败: " + packageToScan, e);
            }
        }
    }
    
    /**
     * 专门注册AcSemester别名
     */
    private void registerAcSemesterAlias(org.apache.ibatis.session.Configuration configuration) {
        TypeAliasRegistry typeAliasRegistry = configuration.getTypeAliasRegistry();
        try {
            Class<?> acSemesterClass = Class.forName("com.zjcloud.academic.domain.AcSemester");
            typeAliasRegistry.registerAlias("AcSemester", acSemesterClass);
            logger.info("专门注册AcSemester别名成功");
        } catch (ClassNotFoundException e) {
            logger.debug("AcSemester类不存在，跳过别名注册: {}", e.getMessage());
        } catch (Exception e) {
            logger.warn("注册AcSemester别名失败: {}", e.getMessage());
        }
    }
}